import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.net.*;

public class URLDownload
{
    public static final int MAX_THREAD_NUMBER=20;//线程最大数目
	
	private String urlString;//下载的URL连接
	private String fileName;//保存文件名
	private int URLFileLength;//下载文件长度
	private URL url;//URL实例
	
	private int timeout=10000;//超时时长
	private boolean isMultiThread=true;//是否启用多线程
	private int usedThreadNumber=5;//启动的线程数量
	
	private MappedByteBuffer[] mapBuffer;//内存映射文件缓冲区
	private InputStream is[];//URL输入流
	
	private int actualThreadNumber;//实际启动的线程数目
	private int sizePerEachThread;//（currentThread-1）个线程下载的数据量
	private int remainingSize;//（currentThread-1）个线程余下的数据量，由最后一个线程下载
	private DownloadThread dlThread[];//下载线程实例
	
	public static void main(String[] args)
	{	
		URLDownload urlDownload=null;
		urlDownload=new URLDownload("http://dev.localhost/1.flac","t.flac");//根据实际情况更改
		urlDownload.setIsMultiThread(true);
		urlDownload.execute();
	}
	
	public URLDownload(String url,String fileName)//构造函数
	{
		
		this.urlString=url;
		this.fileName=fileName;
		
		try
		{
			this.url =new URL(this.urlString);//通过URL字符串构造URL类
		}
		catch(MalformedURLException e)
		{
			e.printStackTrace();
		}
	}
	
	public void setTimeout(int timeout)//设置超时时长
	{
		if(timeout<=0)
		{
			return;
		}
		else
		{
			this.timeout=timeout;
		}
	}
	
	public void setIsMultiThread(boolean isMultiThread)//设置是否启动多线程
	{
		this.isMultiThread=isMultiThread;
	}
	
	public void setmaxThreadNumber(int number)//设置最大线程数
	{
		if(number>URLDownload.MAX_THREAD_NUMBER || number<1)//为保证系统性能，最大线程数最高可设置为20
			this.usedThreadNumber=20;
		else
			this.usedThreadNumber=number;
	}
	
	public void execute()//执行下载
	{
		this.getSourceFileLength();//取资源长度
		this.ThreadDispatch();//线程任务分配
		this.CreateFile();//创建本地文件
		
		this.is=new InputStream[this.actualThreadNumber];
		this.mapBuffer=new MappedByteBuffer[this.actualThreadNumber];
		this.dlThread=new DownloadThread[this.actualThreadNumber];
		
		for(int i=1;i<=this.actualThreadNumber;i++)//创建应有数量的下载线程
		{
			int start=(i-1)*this.sizePerEachThread;//起始点计算
			
			int size;
			if(i!=this.actualThreadNumber)//N-1个线程需要下载的数据量
			{
				size=this.sizePerEachThread;
			}
			else
			{
				size=this.sizePerEachThread+this.remainingSize;//最后一个线程需要下载额外的数据
			}
			
			try
			{
				is[i-1]=this.url.openStream();//为每个线程打开一个URL输入流
			}
			catch(IOException e)
			{
				e.printStackTrace();
			}
			
			this.mapBuffer[i-1]=this.CreateFile();//为每个线程分配一个指向同一个文件的文件指针
			
			this.dlThread[i-1]=new DownloadThread(start,size,this.is[i-1],this.mapBuffer[i-1]);
			new Thread(dlThread[i-1],"DownloadThread"+i).start();
			
		}
		
	}
	
	
	private void CALLBACK_CloseInputStream(InputStream is)//回调函数，线程下载完毕后，关闭当前前程打开的输入流
	{
		try
		{
			is.close();
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
	}
	
	private MappedByteBuffer CreateFile()//创建本地文件，用以保存下载数据;同时，返回一个MappedByteBuffer对象，为多线程准备
	{	
		MappedByteBuffer mappedByteBuffer=null;
		try
		{
			RandomAccessFile file = new RandomAccessFile(this.fileName,"rw");//创建随机存取文件模式
			FileChannel channel = file.getChannel();//创建文件通道
			mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0L, this.URLFileLength).load();//内存映射文件
			channel.close();
			file.close();
			
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
		}
		
		return mappedByteBuffer;
	}
	
	private void getSourceFileLength()//获取URL资源大小
	{
		URLConnection con=null;
		try
		{
			con=this.url.openConnection();
			con.setConnectTimeout(this.timeout);//超时设置
			con.setReadTimeout(this.timeout);
			this.URLFileLength=con.getContentLength();
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
	}
	
	private void ThreadDispatch()//线程调度
	{
		int size=this.URLFileLength/this.usedThreadNumber;//计算计划的每个线程需要下载的数据量
		if(size<1024*1024*2)//限制每个线程下载的数据量至少为2MB（为限制系统开销，每个线程至少执行2MB的下载）
		{
			this.actualThreadNumber=this.URLFileLength/(1024*1024*2);//计算实际所需的线程数（为限制系统开销，每个线程至少执行2MB的下载）
			this.actualThreadNumber=(this.actualThreadNumber>URLDownload.MAX_THREAD_NUMBER) ? URLDownload.MAX_THREAD_NUMBER:this.actualThreadNumber;//限制最大线程数为20
		}
		else
		{
			this.actualThreadNumber=this.usedThreadNumber;
		}
		
		this.sizePerEachThread=this.URLFileLength/actualThreadNumber;//计算每个线程至少需要下载的数据量
		this.remainingSize=this.URLFileLength%actualThreadNumber;//计算余下的数据量
	}
	
	private class DownloadThread implements Runnable//实际可执行多线程下载的类
	{
		private int start;//本线程下载起始点
		private int end;//本线程下载终止点
		private int size;//本线程需要下载的数据量
		
		private InputStream is;//输入流
		private MappedByteBuffer mappedBuffer;//内存映射文件
		
		public DownloadThread(int start,int size,InputStream is,MappedByteBuffer mappedBuffer)
		{
			DownloadThread.this.start=start;
			DownloadThread.this.end=start+size-1;//根据起点和数据长度计算终点
			DownloadThread.this.size=size;
			DownloadThread.this.is=is;
			DownloadThread.this.mappedBuffer=mappedBuffer;
		}
		
		public void run()
		{
			int temp;byte buffer;
			DownloadThread.this.mappedBuffer.position(DownloadThread.this.start);
			try
			{
				DownloadThread.this.is.skip(DownloadThread.this.start);//文件流指针偏移到起始点
				for(int i=1;i<=DownloadThread.this.size;i++)
				{
					temp=is.read();
					if(temp==-1) break;
					buffer=(byte)(temp & 0x0000ff);
					DownloadThread.this.mappedBuffer.put(buffer);
				}
				URLDownload.this.CALLBACK_CloseInputStream(is);//回调，关闭当前线程操作的IO流
			}
			catch(IOException e)
			{
				e.printStackTrace();
			}
			
		}
	}
	
}